<!DOCTYPE html>

<head>
    <title> Cesium.js + TWGL.js </title>
    <!-- Use correct character set. -->
    <meta charset="utf-8" />
    <!-- Tell IE to use the latest, best version. -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
    <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
    <link href="./node_modules/cesium/Build/CesiumUnminified/Widgets/widgets.css" rel="stylesheet">
    <script type="text/javascript" src="./node_modules/cesium/Build/CesiumUnminified/Cesium.js"></script>
    <script type="text/javascript" src="./node_modules/twgl.js/dist/5.x/twgl-full.js"></script>
    <style>
        html,
        body,
        #sceneContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
            background: #000;
        }

        .cesium-performanceDisplay-defaultContainer {
            left: 10px;
            right: auto;
        }
    </style>
</head>

<body>
    <div id="sceneContainer"></div>
    <script type="module">
        (async () => {
            const main = () => {
                /**
                 * 由m3d创作, 仅供学习使用
                 * gitee:https://gitee.com/m3d
                 * b站:https://www.bilibili.com/video/BV1yG4y1m7dF/
                 */
                Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjMzU2ZTQyYy1iOTU5LTQ5MDQtOGNkNC0yYzcxMTI1ZDJiZGQiLCJpZCI6NzY1OTcsImlhdCI6MTYzOTU2MDcwOH0.kbWigipGD6l2OPBGpnkkN6dzp8NuNjoHNNM1NF4gaIo';
                // 初始化Cesium.js
                let scene;
                const initView = () => {
                    const viewer = new Cesium.Viewer("sceneContainer");
                    scene = viewer.scene;
                    scene.globe.depthTestAgainstTerrain = true;
                    scene.logarithmicDepthBuffer = false;
                    // 这里简单模拟 加入自定义流程  不修改源码模拟实际需要修改源码更合适 
                    // 创建一个不透明物体渲染
                    const boxGeometry = new Cesium.BoxGeometry({
                        vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
                        maximum: new Cesium.Cartesian3(1.0, 1.0, 1.0),
                        minimum: new Cesium.Cartesian3(-1.0, -1.0, -1.0)
                    })
                    const opacityBox = viewer.scene.primitives.add(new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: boxGeometry,
                            modelMatrix: generateModelMatrix([120.16549584566921, 30.19520218232935, 1], [0, 0, 0], [1, 1, 1]),
                            attributes: {
                                color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
                            }
                        }),
                        appearance: new Cesium.PerInstanceColorAppearance({ flat: true, translucent: false }),
                        asynchronous: false
                    }));
                    // 劫持渲染pass 加入其他引擎的webgl操作
                    let isExecute = false;
                    viewer.scene.postUpdate.addEventListener(() => {
                        if (!isExecute && opacityBox._colorCommands && opacityBox._colorCommands.length > 0) {
                            const colorCommand = opacityBox._colorCommands[0]
                            const oEexecute = colorCommand.execute;
                            colorCommand.execute = function (context, passState) {
                                oEexecute.bind(this)(context, passState);
                                renderTsgl && renderTsgl();
                            }
                            isExecute = true;
                        }
                    })
                    // 视角定位
                    viewer.camera.setView({
                        destination: Cesium.Cartesian3.fromDegrees(120.16549584566921, 30.18520218232935, 500),
                        orientation: {
                            heading: 6.214644636589376,
                            pitch: -0.5249810907912531,
                            roll: 0.000001089972775147885,
                        }
                    });
                }

                // 初始化Twgl.js
                // 示例: https://github.com/greggman/twgl.js/blob/main/examples/twgl-cube.html
                const m4 = twgl.m4;
                let programInfo;
                let bufferInfo;
                let uniforms;
                const initTwgl = () => {
                    // cesium的webgl上下文
                    const gl = scene.context._gl;
                    // 简单对齐坐标，保证在视图坐标系中是一致的，这里加入enu坐标系
                    const cesiumEnu = Cesium.Matrix4.toArray(generateModelMatrix([120.16549584566921, 30.19520218232935, 80], [0, 0, 0], [100, 100, 100]));
                    // 引入twgl进行webgl渲染
                    const vs = `
                        #version 300 es
                        #define attribute in
                        #define varying out
                        uniform mat4 u_world;
                        uniform mat4 u_cesiumEnu;
                        uniform mat4 u_viewProjection;
                        in vec4 position;
                        in vec2 texcoord;
                        out vec4 v_position;
                        out vec2 v_texCoord;
                        void main() {
                            v_texCoord = texcoord;
                            v_position = u_viewProjection * u_cesiumEnu * u_world * position;
                            gl_Position = v_position;
                        }`;
                    const fs = `
                        #version 300 es
                        precision highp float;
                        in vec2 v_texCoord;
                        uniform sampler2D u_diffuse;
                        layout(location = 0) out vec4 out_FragColor;
                        void main() {
                            vec4 diffuseColor = texture(u_diffuse, v_texCoord);
                            out_FragColor = diffuseColor;
                        }
                        `;
                    // 创建着色器
                    programInfo = twgl.createProgramInfo(gl, [vs, fs]);
                    // 创建vao
                    const arrays = {
                        position: [1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1],
                        normal: [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1],
                        texcoord: [1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1],
                        indices: [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23],
                    };
                    bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
                    // 创建纹理
                    const tex = twgl.createTexture(gl, {
                        min: gl.NEAREST,
                        mag: gl.NEAREST,
                        src: [
                            255, 255, 255, 255,
                            192, 192, 192, 255,
                            192, 192, 192, 255,
                            255, 255, 255, 255,
                        ],
                    });
                    uniforms = {
                        u_cesiumEnu: cesiumEnu,
                        u_diffuse: tex,
                        u_world: [],
                        u_viewProjection: [],
                    };
                }
                // 渲染twgl定义的webgl资源
                const renderTsgl = () => {
                    const gl = scene.context._gl;
                    // 将twgl.js定义的内容绘制cesium的fbo
                    const view = Cesium.Matrix4.toArray(scene.context.uniformState.view);
                    const viewProjection = Cesium.Matrix4.toArray(scene.context.uniformState.viewProjection);
                    const world = m4.rotationY(scene.frameState.frameNumber * 0.001);
                    uniforms.u_world = world;
                    uniforms.u_viewProjection = viewProjection;
                    gl.useProgram(programInfo.program);
                    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
                    twgl.setUniforms(programInfo, uniforms);
                    gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
                }
                initView();
                initTwgl();
            }
            const generateModelMatrix = (position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1]) => {
                const rotationX = Cesium.Matrix4.fromRotationTranslation(
                    Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(rotation[0])));

                const rotationY = Cesium.Matrix4.fromRotationTranslation(
                    Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(rotation[1])));

                const rotationZ = Cesium.Matrix4.fromRotationTranslation(
                    Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(rotation[2])));
                if (!(position instanceof Cesium.Cartesian3)) {
                    position = Cesium.Cartesian3.fromDegrees(...position)
                }
                const enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(position);
                Cesium.Matrix4.multiply(enuMatrix, rotationX, enuMatrix);
                Cesium.Matrix4.multiply(enuMatrix, rotationY, enuMatrix);
                Cesium.Matrix4.multiply(enuMatrix, rotationZ, enuMatrix);
                const scaleMatrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(...scale));
                const modelMatrix = Cesium.Matrix4.multiply(enuMatrix, scaleMatrix, new Cesium.Matrix4());

                return modelMatrix;
            }
            main();
        })();
    </script>
</body>